
`include "common_header.verilog"

//  *************************************************************************
//   File : pcs_tx_state_mc.vhd 
//  *************************************************************************
//   This program is controlled by a written license agreement.
//   Unauthorized Reproduction or Use is Expressly Prohibited. 
// 
//  Copyright (c) 2000 Morethanip GmbH
//  info@morethanip.com
//  *************************************************************************
//  *************************************************************************
//  Version: $Id: check_end.v,v 1.6 2011/08/31 13:10:45 dk Exp $ 
//  Author : Muhammad Anisur Rahman
//  *************************************************************************
//  Description:
// 
//  PCS receive control (inplements check end function and 
//  IDLE code group mapping)
//  *************************************************************************
module check_end (

   reset,
   clk,
  `ifdef USE_CLK_ENA
   clk_ena,
  `endif     
   ln_align,
   kchar0,
   data0,
   kchar1,
   data1,
   kchar2,
   data2,
   kchar3,
   data3,
   ln_align_ckd,
   k_ckd0,
   d_ckd0,
   k_ckd1,
   d_ckd1,
   k_ckd2,
   d_ckd2,
   k_ckd3,
   d_ckd3
`ifdef MTIPXGXS_EEE_ENA
   ,
   rx_lpi_active,
   rx_lpi_ind,
   rx_idle_ind   
`endif   
   
   );

input   reset; //  Active High Reset
input   clk; //  156.25MHz Receive Clock 
`ifdef USE_CLK_ENA
input   clk_ena;
`endif    
input   ln_align; //  Lane Alignment Done
input   [1:0] kchar0; //  Special Character Indication
input   [15:0] data0; //  Decoded Data      
input   [1:0] kchar1; //  Special Character Indication
input   [15:0] data1; //  Decoded Data 
input   [1:0] kchar2; //  Special Character Indication
input   [15:0] data2; //  Decoded Data 
input   [1:0] kchar3; //  Special Character Indication
input   [15:0] data3; //  Decoded Data 
output   ln_align_ckd; //  Lane Alignment Done
output   [1:0] k_ckd0; //  Special Character Indication
output   [15:0] d_ckd0; //  Decoded Data      
output   [1:0] k_ckd1; //  Special Character Indication
output   [15:0] d_ckd1; //  Decoded Data 
output   [1:0] k_ckd2; //  Special Character Indication
output   [15:0] d_ckd2; //  Decoded Data 
output   [1:0] k_ckd3; //  Special Character Indication
output   [15:0] d_ckd3; //  Decoded Data

`ifdef MTIPXGXS_EEE_ENA
input    rx_lpi_active; //  LPI statemachine forcing LPI to XGMII
output   rx_lpi_ind;    //  true indication of LPI decoded
output   rx_idle_ind;   //  true indication of IDLE decoded

reg      rx_lpi_ind;
reg      rx_idle_ind;
`endif   

wire    ln_align_ckd; 
reg     [1:0] k_ckd0; 
reg     [15:0] d_ckd0; 
reg     [1:0] k_ckd1; 
reg     [15:0] d_ckd1; 
reg     [1:0] k_ckd2; 
reg     [15:0] d_ckd2; 
reg     [1:0] k_ckd3; 
reg     [15:0] d_ckd3; 
reg     [1:0] kchar0_d1; 
reg     [15:0] data0_d1; 
reg     [1:0] kchar1_d1; 
reg     [15:0] data1_d1; 
reg     [1:0] kchar2_d1; 
reg     [15:0] data2_d1; 
reg     [1:0] kchar3_d1; 
reg     [15:0] data3_d1; 

reg     [1:0] data0_d2_err; 
reg     [1:0] data1_d2_err; 
reg     [1:0] data2_d2_err; 
reg     data3_d2_err0; 

reg     ln_align_d1; 
reg     ln_align_d2; 

wire [15:0] rxd_tmp0;
wire [15:0] rxd_tmp1;
wire [15:0] rxd_tmp2;
wire [15:0] rxd_tmp3;

wire [1:0] rxc_tmp0;
wire [1:0] rxc_tmp1; 
wire [1:0] rxc_tmp2;
wire [1:0] rxc_tmp3;

`ifdef MTIPXGXS_EEE_ENA

wire [1:0] col_idle_d1; // encode IDLE to XGMII, aligned with d1 pipe
wire [1:0] col_lpi_d1;  // encode LPI to XGMII, aligned with d1 pipe
//reg  [1:0] col_lpi_d2;  // encode LPI to XGMII, aligned with d2 pipe
reg  [1:0] col_lpi_d2f; // encode LPI to XGMII, aligned with d2 pipe, forced with rx_lpi_active

`endif


always @(ln_align_d1 or data0_d2_err or data1_d2_err or data2_d2_err or data3_d2_err0 or
         data1_d1 or kchar1_d1 or data0_d1 or kchar0_d1 or data2_d1 or kchar2_d1 or data3_d1 or kchar3_d1 or
         rxd_tmp0 or rxc_tmp0 or rxd_tmp1 or rxc_tmp1 or rxd_tmp2 or rxc_tmp2 or rxd_tmp3 or rxc_tmp3 
        `ifdef MTIPXGXS_EEE_ENA
         or col_lpi_d2f
        `endif
         )
   begin : process_1    
      
        //  Manipulation for lower column in output data   

        `ifdef MTIPXGXS_EEE_ENA
              if( col_lpi_d2f[0]==1'b 1 )
              begin
                 k_ckd0[0]      = 1'b 1;
                 d_ckd0[7:0]    = 8'h 06;       // LPI
              end
              else                
        `endif  
              if(data0_d2_err[0]==1'b 1 & ln_align_d1==1'b 1)
                 begin
                 k_ckd0[0]      = 1'b 1;
                 d_ckd0[7:0]    = 8'h FE;
                 end
              else  
                 begin
                 k_ckd0[0]      = rxc_tmp0[0];
                 d_ckd0[7:0]    = rxd_tmp0[7:0];
                 end
              
        `ifdef MTIPXGXS_EEE_ENA
              if( col_lpi_d2f[0]==1'b 1 )
              begin
                 k_ckd1[0]      = 1'b 1;
                 d_ckd1[7:0]    = 8'h 06;       // LPI
              end
              else                
        `endif  
              if(data1_d2_err[0] == 1'b 1 & ln_align_d1==1'b 1)
                 begin  
                 k_ckd1[0]      = 1'b 1;	
                 d_ckd1[7:0]    = 8'h FE;
                 end
              else  
                 begin                 
                 k_ckd1[0]      = rxc_tmp1[0];
                 d_ckd1[7:0]    = rxd_tmp1[7:0];
                 end
              
        `ifdef MTIPXGXS_EEE_ENA
              if( col_lpi_d2f[0]==1'b 1 )
              begin
                 k_ckd2[0]      = 1'b 1;
                 d_ckd2[7:0]    = 8'h 06;       // LPI
              end
              else                
        `endif  
              if(data2_d2_err[0] == 1'b 1 & ln_align_d1==1'b 1)
                 begin                
                 k_ckd2[0]      = 1'b 1;
                 d_ckd2[7:0]    = 8'h FE;	
                 end 
              else  
                 begin                 
                 k_ckd2[0]      = rxc_tmp2[0];
                 d_ckd2[7:0]    = rxd_tmp2[7:0];            
                 end
              
        `ifdef MTIPXGXS_EEE_ENA
              if( col_lpi_d2f[0]==1'b 1 )
              begin
                 k_ckd3[0]      = 1'b 1;
                 d_ckd3[7:0]    = 8'h 06;       // LPI
              end
              else                
        `endif  
              if(data3_d2_err0 == 1'b 1 & ln_align_d1==1'b 1)        // [0]
                 begin
                 k_ckd3[0]      = 1'b 1;	
                 d_ckd3[7:0]    = 8'h FE;	
                 end
              else
                 begin
                 k_ckd3[0]      = rxc_tmp3[0];
                 d_ckd3[7:0]    = rxd_tmp3[7:0];
                 end
                
        //  Manipulation for higher column in output data 

        `ifdef MTIPXGXS_EEE_ENA
              if( col_lpi_d2f[1]==1'b 1 )
              begin
                 k_ckd0[1]      = 1'b 1;	
                 d_ckd0[15:8]   = 8'h 06;       // LPI
              end
              else                
        `endif  
              if(data0_d2_err[1]==1'b 1 & ln_align_d1==1'b 1)
                 begin
                 k_ckd0[1]      = 1'b 1;	
                 d_ckd0[15:8]   = 8'h FE;
                 end
              else
                 begin
                 k_ckd0[1]      = rxc_tmp0[1];
                 d_ckd0[15:8]   = rxd_tmp0[15:8];
                 end              
              
        `ifdef MTIPXGXS_EEE_ENA
              if( col_lpi_d2f[1]==1'b 1 )
              begin
                 k_ckd1[1]      = 1'b 1;	
                 d_ckd1[15:8]   = 8'h 06;       // LPI
              end
              else                
        `endif  
              if ((data1_d1[7:0] != 8'h 3C & data1_d1[7:0] != 8'h BC & data1_d1[7:0] != 8'h FC | kchar1_d1[0] == 1'b 0 | data1_d1[7:0] == 8'h FE) & 
                  (data0_d1[7:0] == 8'h FD & kchar0_d1[0] == 1'b 1) & ln_align_d1==1'b 1)
                 begin
                 k_ckd1[1]      = 1'b 1;	
                 d_ckd1[15:8]   = 8'h FE;	
                 end
              else if(data1_d2_err[1] == 1'b 1 & ln_align_d1==1'b 1)
                 begin
                 k_ckd1[1]      = 1'b 1;
                 d_ckd1[15:8]   = 8'h FE;
                 end
              else
                 begin
                 k_ckd1[1]      = rxc_tmp1[1];
                 d_ckd1[15:8]   = rxd_tmp1[15:8];
                 end
              
        `ifdef MTIPXGXS_EEE_ENA
              if( col_lpi_d2f[1]==1'b 1 )
              begin
                 k_ckd2[1]      = 1'b 1;	
                 d_ckd2[15:8]   = 8'h 06;       // LPI
              end
              else                
        `endif  
              if ((data2_d1[7:0] != 8'h 3C & data2_d1[7:0] != 8'h BC & 
        	data2_d1[7:0] != 8'h FC | kchar2_d1[0] == 1'b 0 | 
        	data2_d1[7:0] == 8'h FE) & (data0_d1[7:0] == 8'h FD & 
        	kchar0_d1[0] == 1'b 1 | data1_d1[7:0] == 8'h FD & 
        	kchar1_d1[0] == 1'b 1) & ln_align_d1==1'b 1)
                 begin
                 k_ckd2[1]      = 1'b 1;	
                 d_ckd2[15:8]   = 8'h FE;	
                 end
              else if(data2_d2_err[1] == 1'b 1 & ln_align_d1==1'b 1)
                 begin
                 k_ckd2[1]      = 1'b 1;
                 d_ckd2[15:8]   = 8'h FE;
                 end
              else
                 begin
                 k_ckd2[1]      = rxc_tmp2[1];
                 d_ckd2[15:8]   = rxd_tmp2[15:8];
                 end
              
        `ifdef MTIPXGXS_EEE_ENA
              if( col_lpi_d2f[1]==1'b 1 )
              begin
                 k_ckd3[1]      = 1'b 1;	
                 d_ckd3[15:8]   = 8'h 06;       // LPI
              end
              else                
        `endif  
              if ((data3_d1[7:0] != 8'h 3C & data3_d1[7:0] != 8'h BC & 
        	data3_d1[7:0] != 8'h FC | kchar3_d1[0] == 1'b 0 | 
        	data3_d1[7:0] == 8'h FE) & (data0_d1[7:0] == 8'h FD & 
        	kchar0_d1[0] == 1'b 1 | data1_d1[7:0] == 8'h FD & 
        	kchar1_d1[0] == 1'b 1 | data2_d1[7:0] == 8'h FD & 
        	kchar2_d1[0] == 1'b 1) & ln_align_d1==1'b 1)
                 begin
                 k_ckd3[1]      = 1'b 1;	
                 d_ckd3[15:8]   = 8'h FE;	
                 end
              //else if(data3_d2_err[1] == 1'b 1)
              //   begin
              //   k_ckd3[1]      <= 1'b 1;
              //   d_ckd3[15:8]   <= 8'h FE;
              //   end
              else
                 begin
                 k_ckd3[1]      = rxc_tmp3[1];
                 d_ckd3[15:8]   = rxd_tmp3[15:8];
                 end
   end


always @(posedge reset or posedge clk)
   begin : process_2
   if (reset == 1'b 1)
      begin
      data0_d2_err <= 2'b 00;	
      data1_d2_err <= 2'b 00;	
      data2_d2_err <= 2'b 00;	
      //data3_d2_err <= 2'b 00;	
      data3_d2_err0 <= 1'b 0;	
      end
   else
      begin

         `ifdef USE_CLK_ENA
            if(clk_ena == 1'b 1)
            begin
         `endif 

        //  Manipulation for lower column in intermediate data 
              if ((data0_d1[15:8] != 8'h 3C & data0_d1[15:8] != 8'h BC & data0_d1[15:8] != 8'h FC & data0_d1[15:8] != 8'h 7C | 
        	   kchar0_d1[1] == 1'b 0 | data0_d1[15:8] == 8'h FE) &
        	  (data1_d1[7:0] == 8'h FD & kchar1_d1[0] == 1'b 1 | 
        	data2_d1[7:0] == 8'h FD & kchar2_d1[0] == 1'b 1 | 
        	data3_d1[7:0] == 8'h FD & kchar3_d1[0] == 1'b 1))
                 begin
                 data0_d2_err[0] <= 1'b 1;
                 end
              else
                 begin
                 data0_d2_err[0] <= 1'b 0;
                 end
              
              if ((data1_d1[15:8] != 8'h 3C & data1_d1[15:8] != 8'h BC & 
        	data1_d1[15:8] != 8'h FC | kchar1_d1[1] == 1'b 0 | 
        	data1_d1[15:8] == 8'h FE) & (data0_d1[15:8] == 8'h FD & 
        	kchar0_d1[1] == 1'b 1))
                 begin
                 data1_d2_err[0] <= 1'b 1;
                 end
              else if ((data1_d1[15:8] != 8'h 3C & data1_d1[15:8] != 8'h BC & 
        	data1_d1[15:8] != 8'h FC & data1_d1[15:8] != 8'h 7C | 
        	kchar1_d1[1] == 1'b 0 | data1_d1[15:8] == 8'h FE) & 
        	(data2_d1[7:0] == 8'h FD & kchar2_d1[0] == 1'b 1 | 
        	data3_d1[7:0] == 8'h FD & kchar3_d1[0] == 1'b 1) )
                 begin
                 data1_d2_err[0] <= 1'b 1;
                 end
              else
                 begin
                 data1_d2_err[0] <= 1'b 0;
                 end

              if ((data2_d1[15:8] != 8'h 3C & data2_d1[15:8] != 8'h BC & 
        	data2_d1[15:8] != 8'h FC | kchar2_d1[1] == 1'b 0 | 
        	data2_d1[15:8] == 8'h FE) & (data0_d1[15:8] == 8'h FD & 
        	kchar0_d1[1] == 1'b 1 | data1_d1[15:8] == 8'h FD & 
        	kchar1_d1[1] == 1'b 1))
                 begin
                 data2_d2_err[0] <= 1'b 1;
                 end
              else if ((data2_d1[15:8] != 8'h 3C & data2_d1[15:8] != 8'h BC & 
        	data2_d1[15:8] != 8'h FC & data2_d1[15:8] != 8'h 7C | 
        	kchar2_d1[1] == 1'b 0 | data2_d1[15:8] == 8'h FE) & 
        	(data3_d1[7:0] == 8'h FD & kchar3_d1[0] == 1'b 1) )
                 begin
                 data2_d2_err[0] <= 1'b 1;
                 end
              else
                 begin
                 data2_d2_err[0] <= 1'b 0;
                 end

              if ((data3_d1[15:8] != 8'h 3C & data3_d1[15:8] != 8'h BC & 
        	data3_d1[15:8] != 8'h FC | kchar3_d1[1] == 1'b 0 | 
        	data3_d1[15:8] == 8'h FE) & (data0_d1[15:8] == 8'h FD & 
        	kchar0_d1[1] == 1'b 1 | data1_d1[15:8] == 8'h FD & 
        	kchar1_d1[1] == 1'b 1 | data2_d1[15:8] == 8'h FD & 
        	kchar2_d1[1] == 1'b 1))
                 begin
                 data3_d2_err0 <= 1'b 1;	// [0]
                 end
              else
                 begin
                 data3_d2_err0 <= 1'b 0;	// [0]
                 end

        //  Manipulation for higher column in intermediate data 

              //data3_d2_err[1] <= 1'b 0;	

              if ((data0[7:0] != 8'h 3C & data0[7:0] != 8'h BC & 
        	data0[7:0] != 8'h FC & data0[7:0] != 8'h 7C | 
        	kchar0[0] == 1'b 0 | data0[7:0] == 8'h FE) & 
        	(data1_d1[15:8] == 8'h FD & kchar1_d1[1] == 1'b 1 | 
        	data2_d1[15:8] == 8'h FD & kchar2_d1[1] == 1'b 1 | 
        	data3_d1[15:8] == 8'h FD & kchar3_d1[1] == 1'b 1))
                 begin
                 data0_d2_err[1] <= 1'b 1;
                 end
              else
                 begin
                 data0_d2_err[1] <= 1'b 0;
                 end

              if ((data1[7:0] != 8'h 3C & data1[7:0] != 8'h BC & 
        	data1[7:0] != 8'h FC & data1[7:0] != 8'h 7C | 
        	kchar1[0] == 1'b 0 | data1[7:0] == 8'h FE) & 
        	(data2_d1[15:8] == 8'h FD & kchar2_d1[1] == 1'b 1 | 
        	data3_d1[15:8] == 8'h FD & kchar3_d1[1] == 1'b 1))
                 begin
                 data1_d2_err[1] <= 1'b 1;	
                 end
              else
                 begin
                 data1_d2_err[1] <= 1'b 0;	
                 end

              if ((data2[7:0] != 8'h 3C & data2[7:0] != 8'h BC & 
        	data2[7:0] != 8'h FC & data2[7:0] != 8'h 7C | 
        	kchar2[0] == 1'b 0 | data2[7:0] == 8'h FE) & 
        	(data3_d1[15:8] == 8'h FD & kchar3_d1[1] == 1'b 1))
                 begin
                 data2_d2_err[1] <= 1'b 1;
                 end
              else
                 begin
                 data2_d2_err[1] <= 1'b 0;
                 end
      
         `ifdef USE_CLK_ENA
            end
         `endif      
      
      end
   end

always @(posedge reset or posedge clk)
   begin : process_3
   if (reset == 1'b 1)
      begin
      data0_d1 <= {16{1'b 0}};	
      kchar0_d1 <= {2{1'b 0}};	
      data1_d1 <= {16{1'b 0}};	
      kchar1_d1 <= {2{1'b 0}};	
      data2_d1 <= {16{1'b 0}};	
      kchar2_d1 <= {2{1'b 0}};	
      data3_d1 <= {16{1'b 0}};	
      kchar3_d1 <= {2{1'b 0}};	
      end
   else
      begin
      
         `ifdef USE_CLK_ENA
            if(clk_ena == 1'b 1)
            begin
         `endif       
      
              data0_d1 <= data0;	
              kchar0_d1 <= kchar0;	
              data1_d1 <= data1;	
              kchar1_d1 <= kchar1;	
              data2_d1 <= data2;	
              kchar2_d1 <= kchar2;	
              data3_d1 <= data3;	
              kchar3_d1 <= kchar3;	
      
         `ifdef USE_CLK_ENA
            end
         `endif      
      
      end
   end

always @(posedge reset or posedge clk)
   begin : process_4
   if (reset == 1'b 1)
      begin
      ln_align_d2  <= 1'b 0;	
      ln_align_d1  <= 1'b 0;	
      end
   else
      begin
      
         `ifdef USE_CLK_ENA
            if(clk_ena == 1'b 1)
            begin
         `endif       
      
              ln_align_d1  <= ln_align;
              ln_align_d2  <= ln_align_d1;              
      
         `ifdef USE_CLK_ENA
            end
         `endif      
      
      end
   end

assign ln_align_ckd = ln_align_d2;


// IDLE Code group mapping for Lower column
xgxs_rx_cntl_col U_MAP0(

   .reset(reset),
   .clk(clk),
  `ifdef USE_CLK_ENA
   .clk_ena(clk_ena),
  `endif   
   .ln_align(ln_align_d1),
   .deskew_rxd({data3_d1[7:0],data2_d1[7:0],data1_d1[7:0],data0_d1[7:0]}),
   .deskew_rxc({kchar3_d1[0], kchar2_d1[0], kchar1_d1[0], kchar0_d1[0]}),
   .rxd({rxd_tmp3[7:0], rxd_tmp2[7:0], rxd_tmp1[7:0], rxd_tmp0[7:0]}),
   .rxc({rxc_tmp3[0], rxc_tmp2[0], rxc_tmp1[0], rxc_tmp0[0]}));

// IDLE Code group mapping for Higher column
xgxs_rx_cntl_col U_MAP1(

   .reset(reset),
   .clk(clk),
  `ifdef USE_CLK_ENA
   .clk_ena(clk_ena),
  `endif   
   .ln_align(ln_align_d1),
   .deskew_rxd({data3_d1[15:8],data2_d1[15:8],data1_d1[15:8],data0_d1[15:8]}),
   .deskew_rxc({kchar3_d1[1], kchar2_d1[1], kchar1_d1[1], kchar0_d1[1]}),
   .rxd({rxd_tmp3[15:8], rxd_tmp2[15:8], rxd_tmp1[15:8], rxd_tmp0[15:8]}),
   .rxc({rxc_tmp3[1], rxc_tmp2[1], rxc_tmp1[1], rxc_tmp0[1]}));

`ifdef MTIPXGXS_EEE_ENA
        
        wire sw_reset;
        assign sw_reset = 1'b 0;
        
        // determine LPI character and indicate need for mapping into full column.

        xgxs_col_lpidet16 U_LPIDET16 (
        
                .reset(reset),
                .sw_reset(sw_reset),
                .clk(clk),
                .clk_ena(clk_ena),
                .kchar0(kchar0),
                .data0(data0),
                .kchar1(kchar1),
                .data1(data1),
                .kchar2(kchar2),
                .data2(data2),
                .kchar3(kchar3),
                .data3(data3),
                .col_idle(col_idle_d1), // is one delayed, hence same as d1 data pipes
                .col_lpi(col_lpi_d1) ); // is one delayed, hence same as d1 data pipes

        always @(posedge reset or posedge clk)
        begin : process_lpi
                if (reset == 1'b 1)
                begin
                        
                        //col_lpi_d2  <= 2'b 00;
                        col_lpi_d2f <= 2'b 00;
                        rx_lpi_ind  <= 1'b 0;
                        rx_idle_ind <= 1'b 0;
                        
                end
                else
                 `ifdef USE_CLK_ENA
                    if(clk_ena == 1'b 1)
                 `endif       
                begin
                        // align with d2 pipe (output of rx_cntl_col)
                        
                        if( rx_lpi_active==1'b 1 )
                        begin
                                // force output to LPI during low power
                                
                                col_lpi_d2f <= 2'b 11;
                        end
                        else
                        begin
                                col_lpi_d2f <= col_lpi_d1;
                        end

                        // give true LPI decode indication to LPI statemachine, aligned with XGMII output
                        
                        //col_lpi_d2 <= col_lpi_d1;      // align with pipeline to output (but omitting rx_lpi_active forced)
                        
                        if( col_lpi_d1 != 2'b 00 )
                        begin
                                rx_lpi_ind  <= 1'b 1;
                        end
                        else
                        begin
                                rx_lpi_ind  <= 1'b 0;
                        end
                        
                        // give true IDLE decode indication to LPI statemachine
                        // ensure it does not overlap with lpi_ind due to two columns inspected at same time
                        
                        if( col_idle_d1 != 2'b 00 & col_lpi_d1 == 2'b 00 & ln_align_d1==1'b 1)
                        begin
                                rx_idle_ind <= 1'b 1;
                        end
                        else
                        begin
                                rx_idle_ind <= 1'b 0;
                        end

                end
        end

                
`endif


endmodule // module check_end

